﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace AntUnion.FrameWork.Util
{
    /// <summary>
    /// 加密操作类
    /// </summary>
    public class SecurityUtil
    {
        #region UrlEncode编码

        public static string UrlEncode(string str)
        {
            StringBuilder builder = new StringBuilder();
            foreach (char c in str)
            {
                if (HttpUtility.UrlEncode(c.ToString()).Length > 1)
                {
                    builder.Append(HttpUtility.UrlEncode(c.ToString()).ToUpper());
                }
                else
                {
                    builder.Append(c);
                }
            }
            return builder.ToString();
        }

        #endregion

        #region 加密相关

        /// MD5加密 
        /// </summary> 
        /// <param name="str"></param> 
        /// <returns></returns> 
        public static string Md5(string str)
        {
            MD5 md5Hasher = MD5.Create();
            byte[] data = md5Hasher.ComputeHash(Encoding.GetEncoding("UTF-8").GetBytes(str));
            return BitConverter.ToString(data).Replace("-", "").ToLower(); //可以直接使用这个方法 
        }

        /// Sha1加密 
        /// </summary> 
        /// <param name="str"></param> 
        /// <returns></returns> 
        public static string Sha1(string str)
        {
            SHA1 sha1Hasher = SHA1.Create();
            byte[] data = sha1Hasher.ComputeHash(Encoding.GetEncoding("UTF-8").GetBytes(str));
            return BitConverter.ToString(data).Replace("-", "").ToLower();
        }


        public static string EncryptPwd(string str)
        {
            return Md5(Sha1(str) + "3hz7kvnL0TdjNCZ1SasEKiJODmu6f4IUGY8cxlPw");
        }

        /// <summary>
        /// HMACSHA1加密
        /// </summary>
        /// <param name="text">要加密的原串</param>
        ///<param name="key">私钥</param>
        /// <returns></returns>
        public static string HmacSHA1(string text, string key)
        {
            //HMACSHA1加密
            HMACSHA1 hmacsha1 = new HMACSHA1();
            hmacsha1.Key = System.Text.Encoding.UTF8.GetBytes(key);

            byte[] dataBuffer = System.Text.Encoding.UTF8.GetBytes(text);
            byte[] hashBytes = hmacsha1.ComputeHash(dataBuffer);

            var enText = new StringBuilder();
            foreach (byte iByte in hashBytes)
            {
                enText.AppendFormat("{0:x2}", iByte);
            }
            return enText.ToString();

        }

        #endregion

        #region 签名组装

        /// <summary>
        /// 将对象改为keyValue形式，字段间用$分隔，null不加入拼接
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="t">对象</param>
        /// <returns>keyValue字符串</returns>
        public static string objToKeyValue<T>(T t, string[] noSignature, string symbol, bool isOrder, bool isKeyValue)
        {
            string tStr = string.Empty;
            if (t == null)
            {
                return tStr;
            }
            PropertyInfo[] properties;
            if (isOrder)
            {
                properties = t.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).OrderBy(p => p.Name).ToArray();
            }
            else
            {
                properties = t.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            }
            if (properties.Length <= 0)
            {
                return tStr;
            }
            bool firstTag = true;
            foreach (PropertyInfo item in properties)
            {
                string name = item.Name;
                object value = item.GetValue(t, null);
                if (value != null)
                {
                    if (item.PropertyType.IsValueType || item.PropertyType.Name.StartsWith("String"))
                    {
                        if (!string.IsNullOrEmpty(value.ToString()))
                        {
                            if (!noSignature.Contains(item.Name))
                            {
                                if (isKeyValue)
                                    tStr += firstTag ? string.Format("{0}={1}", name, value) : string.Format(symbol + "{0}={1}", name, value);
                                else
                                    tStr += firstTag ? string.Format("{0}", value) : string.Format(symbol + "{0}", value);
                                firstTag = false;
                            }
                        }
                    }
                    else
                    {
                        objToKeyValue(value, noSignature, symbol, isOrder, isKeyValue);
                    }
                }
            }
            return tStr;
        }


        /// <summary>
        /// 将对象改为keyValue形式+秘钥，字段间用$分隔，null不加入拼接
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="t">对象</param>
        /// <param name="secretKey">秘钥</param>
        /// <returns>keyValue字符串</returns>
        public static string objToKeyValue<T>(T t, string[] noSignature, string symbol, bool isOrder, bool isKeyValue, string secretKey)
        {
            return objToKeyValue(t, noSignature, symbol, isOrder, isKeyValue) + secretKey;
        }

        #endregion

        #region 签名组装+MD5

        /// <summary>
        /// 将对象改为密文，字段间用$分隔，null不加入拼接，做MD5加密
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="t">对象</param>
        /// <returns>密文</returns>
        public static string objToMD5Sign<T>(T t, string[] noSignature, string symbol, bool isOrder, bool isKeyValue, out string keyValue)
        {
            keyValue = objToKeyValue(t, noSignature, symbol, isOrder, isKeyValue);
            return Md5(keyValue);
        }

        /// <summary>
        /// 将对象改为密文，字段间用$分隔，null不加入拼接，做MD5加密
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="t">对象</param>
        /// <param name="secretKey">秘钥</param>
        /// <returns>密文</returns>
        public static string objToMD5Sign<T>(T t, string[] noSignature, string symbol, bool isOrder, bool isKeyValue, string secretKey, out string keyValue)
        {
            keyValue = objToKeyValue(t, noSignature, symbol, isOrder, isKeyValue, secretKey);
            return Md5(keyValue);
        }

        /// <summary>
        /// 验证签名
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="t">对象</param>
        /// <param name="sign">密文</param>
        /// <returns>true验签成功，false验签失败</returns>
        public static bool validateMD5Sign<T>(T t, string[] noSignature, string symbol, bool isOrder, bool isKeyValue, string sign, out string keyValue)
        {
            return objToMD5Sign(t, noSignature, symbol, isOrder, isKeyValue, out keyValue) == sign;
        }

        /// <summary>
        /// 验证签名
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="t">对象</param>
        /// <param name="sign">密文</param>
        /// <returns>true验签成功，false验签失败</returns>
        public static bool validateMD5Sign<T>(T t, string[] noSignature, string symbol, bool isOrder, bool isKeyValue, string secretKey, string sign, out string keyValue)
        {
            return objToMD5Sign(t, noSignature, symbol, isOrder, isKeyValue, secretKey, out keyValue) == sign;
        }

        public static bool validateMD5Sign_Upper<T>(T t, string[] noSignature, string symbol, bool isOrder, bool isKeyValue, string secretKey, string sign, out string keyValue)
        {
            return objToMD5Sign(t, noSignature, symbol, isOrder, isKeyValue, secretKey, out keyValue).ToUpper() == sign;
        }

        #endregion

        #region urlEncode+HmacSHA1

        public static string objToHmacSHA1Sign<T>(T t, string[] noSignature, string symbol, bool isOrder, bool isKeyValue, string secretKey, out string keyValue)
        {
            string sign = "";
            keyValue = objToKeyValue(t, noSignature, symbol, isOrder, isKeyValue);
            //urlEncode编码(UTF - 8)
            sign = UrlEncode(keyValue);
            sign = HmacSHA1(sign, secretKey);
            return sign;
        }

        public static bool validateHmacSHA1Sign<T>(T t, string[] noSignature, string symbol, bool isOrder, bool isKeyValue, string secretKey, string sign, out string keyValue)
        {
            return objToHmacSHA1Sign(t, noSignature, symbol, isOrder, isKeyValue, secretKey, out keyValue) == sign;
        }
        #endregion

        #region DES加密

        /// <summary>
        /// DES AES Blowfish
        ///  对称加密算法的优点是速度快，
        ///  缺点是密钥管理不方便，要求共享密钥。
        /// 可逆对称加密  密钥长度8
        /// </summary>
        private static string sKey = "00000000";

        /// <summary>
        /// DES加密
        /// </summary>
        /// <param name="str">需要加密的</param>
        /// <param name="sKey">密匙</param>
        /// <returns></returns>
        public static  string Encrypt(string str)
        {
            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            byte[] inputByteArray = Encoding.Default.GetBytes(str);
            des.Key = ASCIIEncoding.ASCII.GetBytes(sKey);// 密匙
            des.IV = ASCIIEncoding.ASCII.GetBytes(sKey);// 初始化向量
            MemoryStream ms = new MemoryStream();
            CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
            cs.Write(inputByteArray, 0, inputByteArray.Length);
            cs.FlushFinalBlock();
            var retB = Convert.ToBase64String(ms.ToArray());
            return retB;
        }

        /// <summary>
        /// DES解密
        /// </summary>
        /// <param name="pToDecrypt">需要解密的</param>
        /// <param name="sKey">密匙</param>
        /// <returns></returns>
        public static string Decrypt(string pToDecrypt)
        {
            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            byte[] inputByteArray = Convert.FromBase64String(pToDecrypt);
            des.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
            des.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
            MemoryStream ms = new MemoryStream();
            CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write);
            cs.Write(inputByteArray, 0, inputByteArray.Length);
            // 如果两次密匙不一样，这一步可能会引发异常
            cs.FlushFinalBlock();
            return System.Text.Encoding.Default.GetString(ms.ToArray());
        }

        #endregion
    }
}
